#pragma once

#ifdef __linux__
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#else	// __WIN32__
#include <WinSock2.h>
#endif
#include "com.h"

#define MAX_UDP_SEND_SIZE 	65506 	//UDP发送的最大长度,UDP最大数据包长66535-20-8=65507,为避免单字节再减去1=65506

namespace KRPC {

class UDP : public COM
{
public:
	UDP(std::string dstIp, uint16_t dstPort)
	{
#ifdef __WIN32__
		WSAData ws;
		WSAStartup(MAKEWORD(2,2), &ws);
#endif
		if ((sockfd = socket(PF_INET, SOCK_DGRAM, 0)) != -1) {
			int bufsize = MAX_UDP_SEND_SIZE;
			if(-1 == setsockopt(sockfd, SOL_SOCKET, SO_RCVBUF, (const char *)&bufsize, sizeof(int))){/*fix Coverity*/}
			if(-1 == setsockopt(sockfd, SOL_SOCKET, SO_SNDBUF, (const char *)&bufsize, sizeof(int))){/*fix Coverity*/}
		}
		setDstAddr(dstIp, dstPort);

		buf = (char *)malloc(MAX_UDP_SEND_SIZE);
		if(!buf)
			buf = NULL;
	}

	~UDP()
	{
		if (sockfd == -1)
			return;
#ifdef __linux__
		close(sockfd);
#else	//__WIN32__
		closesocket(sockfd);
		WSACleanup();
#endif
		if(buf)
		{
			free(buf);
			buf = NULL;
		}
	}

	virtual bool recv(Packet &packet, int msec) override
	{
		setRcvTimeout(msec);
		//char buf[MAX_UDP_SEND_SIZE] = {0};    //Coverity 栈内存过大
		if(!buf)
			return false;
		memset(buf,0,MAX_UDP_SEND_SIZE);
		auto rcvsz = ::recv(sockfd, buf, MAX_UDP_SEND_SIZE, 0);
		const MsgHead *h = (const MsgHead *)buf;
		if (rcvsz >= (ssize_t)sizeof(MsgHead) && h->len == rcvsz) {
			packet.resize(h->len);
			packet.reset();
			packet.write(buf, h->len);
			return true;
		} else {
			return false;
		}
	}

	void setRcvTimeout(int msec)
	{
#ifdef __linux__
		struct timeval tv = {msec / 1000, 1000 * (msec % 1000)};
		if(-1 == setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv))){/*fix Coverity*/}
#else 	//WINDOWS设置超时参数为一个整数
		setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, (const char *)&msec, sizeof(msec));
#endif
	}

	virtual bool send(const Packet &packet) override
	{
		auto sndsz = (ssize_t)packet.size();
		return (sndsz > MAX_UDP_SEND_SIZE) ? false : \
			(::sendto(sockfd, (const char *)packet.data(), sndsz, 0, (const struct sockaddr *)&dstaddr, sizeof(struct sockaddr)) == sndsz);
	}
	
	virtual bool isLocal() const 
	{
		return dstaddr.sin_addr.s_addr == 0x100007f;//0x100007f为127.0.0.1的网络字节序
	}

	void setDstAddr(const std::string &dstIp, uint16_t dstPort)
	{
		memset(&dstaddr, 0, sizeof(dstaddr));
		dstaddr.sin_family = PF_INET;
		dstaddr.sin_addr.s_addr = inet_addr(dstIp.c_str());
		dstaddr.sin_port = htons(dstPort);
	}

private:
	char *buf;
	int sockfd;
	struct sockaddr_in dstaddr;
};

}
